package com.googlecode.animator.model
{

	import com.googlecode.animator.data.Notifications;
	import com.googlecode.animator.data.ProjectSettingsVO;
	import com.googlecode.caurina.transitions.Tweener;
	import com.googlecode.data.ProjectConstants;
	import com.googlecode.data.cameras.ActualViewCameraVO;
	import com.googlecode.data.cameras.CameraVO;
	import com.googlecode.data.cameras.FrontViewCameraVO;
	import com.googlecode.data.cameras.LeftViewCameraVO;
	import com.googlecode.data.cameras.MainViewCameraVO;
	import com.googlecode.data.cameras.TopViewCameraVO;
	import com.googlecode.data.markers.GlobalMarkerVO;
	import com.googlecode.data.markers.HideMarkerVO;
	import com.googlecode.data.markers.KeyframeVO;
	import com.googlecode.data.materials.MaterialVO;
	import com.googlecode.data.materials.MovieMaterialVO;
	import com.googlecode.data.materials.VideoStreamMaterialVO;
	import com.googlecode.data.objects3d.CameraProxyVO;
	import com.googlecode.data.objects3d.CubeVO;
	import com.googlecode.data.objects3d.MaterialsListObject3DVO;
	import com.googlecode.data.objects3d.Object3DVO;
	import com.googlecode.data.objects3d.PlaneVO;
	import com.googlecode.data.objects3d.SingleMaterialObject3DVO;
	import com.googlecode.data.objects3d.SphereVO;
	import com.googlecode.data.objects3d.TargetProxyVO;
	import com.googlecode.data.viewports.ViewportVO;
	import com.googlecode.utils.UniqueID;
	
	import flash.events.Event;
	import flash.utils.Dictionary;
	
	import mx.collections.ArrayCollection;
	import mx.collections.Sort;
	import mx.collections.SortField;
	
	import org.papervision3d.core.math.Number3D;
	import org.papervision3d.materials.BitmapFileMaterial;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.render.BasicRenderEngine;
	import org.papervision3d.scenes.Scene3D;
	import org.puremvc.as3.multicore.patterns.proxy.Proxy;
	
	/**
	 * Stores project settings.
	 * 
	 * Use 'settings' property instead of getData() for a typed reference to the data stored by the proxy.
	 * 
	 * @author Martin
	 * 
	 */
	public class ProjectSettingsProxy extends Proxy
	{
		/**
		 * PureMVC identifier 
		 */		
		public static const NAME:String = "ProjectSettingsProxy";
		
		/**
		 * Generates a unique number based on the time and date. 
		 */
		private var _uniqueID:UniqueID;
		
		/**
		 * Constructor 
		 * @param proxyName
		 * @param data
		 */
		public function ProjectSettingsProxy()
		{
			super(NAME, new ProjectSettingsVO());
			
			_uniqueID = new UniqueID();
			
			// monitor the status of bitmaps loaded to BitmapFileMaterial
			BitmapFileMaterial.callback = onFileLoadComplete;
		}
		
		///////////////////////////////////////////////
		//
		// 3D world
		//
		///////////////////////////////////////////////
		
		public function set stageWidth(value:uint):void
		{
			settings.stageProperties.stageWidth = value;
			
			sendNotification(Notifications.STAGE_WIDTH_UPDATED_MODEL, settings.stageProperties.clone());
		}
		
		public function get stageWidth():uint
		{
			return settings.stageProperties.stageWidth;
		}
		
		public function set stageHeight(value:uint):void
		{
			settings.stageProperties.stageHeight = value;
			
			sendNotification(Notifications.STAGE_HEIGHT_UPDATED_MODEL, settings.stageProperties.clone());
		}
		
		public function get stageHeight():uint
		{
			return settings.stageProperties.stageHeight;
		}
		
		///////////////////////////////////////////////
		//
		// 3D world
		//
		///////////////////////////////////////////////
		
		public function addScene():void
		{
			settings.scene = new Scene3D();
			settings.renderer = new BasicRenderEngine();
			
			sendNotification(Notifications.SCENE_ADDED);
		}
		
		/**
		 * Returns the one only Scene3D. 
		 * @return 
		 * 
		 */
		public function get scene():Scene3D
		{
			return settings.scene;
		}
		
		/**
		 * Returns the basic render engine. 
		 * @return 
		 * 
		 */
		public function get renderer():BasicRenderEngine
		{
			return settings.renderer;
		}
		
		/**
		 * Sets the position of an object3D in the scene to that specified. 
		 * 
		 */
		protected function setPosObject3DInScene(name:String, pos:Number3D):void
		{
			var scene:Scene3D = settings.scene;
			
			var object3D:DisplayObject3D = scene.getChildByName(name);
			
			object3D.x = pos.x;
			object3D.y = pos.y;
			object3D.z = pos.z;
			
			//sendNotification(Notifications.OBJECT3D_MOVED, name);
		}
		
		/**
		 * Returns the current position of an object3D in the scene. 
		 * @param name
		 * @return 
		 * 
		 */
		public function getPosObject3DInScene(name:String):Number3D
		{
			var scene:Scene3D = settings.scene;
			
			var object3D:DisplayObject3D = scene.getChildByName(name);
			
			var position:Number3D = new Number3D(object3D.x, object3D.y, object3D.z);
			
			return position;
		}
		
		/**
		 * Makes an object3D in the scene either visible or invisible. 
		 * @param name
		 * @param value
		 * 
		 */
		public function setObject3DVisible(name:String, value:Boolean):void
		{
			var scene:Scene3D = settings.scene;
			
			var object3D:DisplayObject3D = scene.getChildByName(name);
			
			object3D.visible = value;
		}
		
		///////////////////////////////////////////////
		//
		// Cameras
		//
		///////////////////////////////////////////////
		
		/**
		 * Adds a Camera3D VO to array. 
		 * @param camera
		 * 
		 */
		public function addCamera(camera:CameraVO):void
		{
			settings.cameras[camera.name] = camera.clone();
			sendNotification(Notifications.CAMERA_ADDED_MODEL, camera.clone());
			
			// relies on the fact that the default cameras are added first
			if(numCameras() == 5)
			{	
						
				sendNotification(Notifications.DEFAULT_CAMERAS_ADDED_MODEL);
			}
		}
		
		/**
		 * Returns a CameraVO for a given camera name. 
		 * @param name
		 * 
		 */
		public function getCamera(name:String):CameraVO
		{	
			return settings.cameras[name].clone();
		}
		
		/**
		 * Set the position of a specified camera. 
		 * @param name
		 * @param pos
		 * @param target
		 * 
		 */
		public function setPosCamera(name:String, pos:Number3D, target:Number3D=null):void
		{
			(settings.cameras[name] as CameraVO).x = pos.x;
			(settings.cameras[name] as CameraVO).y = pos.y;
			(settings.cameras[name] as CameraVO).z = pos.z;
			
			if(target != null)
			{
				(settings.cameras[name] as CameraVO).target.x = target.x;
				(settings.cameras[name] as CameraVO).target.y = target.y;
				(settings.cameras[name] as CameraVO).target.z = target.z;
			}

			sendNotification(Notifications.CAMERA_POSITION_CHANGED_MODEL, name);
		}
		
		/**
		 * Set the zoom of a specified camera. 
		 * @param name
		 * @param zoom
		 * 
		 */
		public function setZoomCamera(name:String, zoom:uint):void
		{
			(settings.cameras[name] as CameraVO).zoom = zoom;
		}
		/**
		 * Returns the list of cameras as a Dictionary. 
		 * @return 
		 * 
		 */
		public function get cameras():Dictionary
		{
			
			return settings.cameras;
		}
		
		public function numCameras():uint
		{
			var i:uint = 0;
			
			for each(var camera:CameraVO in cameras)
			{
				i++;
			}
			
			return i;
		}
		///////////////////////////////////////////////
		//
		// 3D Objects
		//
		///////////////////////////////////////////////
		
		/**
		 * Adds a DisplayObject3D VO to array. 
		 * @param camera
		 * 
		 */
		public function addObject3D(object3D:Object3DVO):void
		{	
			settings.objects3D[object3D.name] = object3D.clone();
			
			sendNotification(Notifications.OBJECT3D_ADDED_MODEL, object3D.clone());
		
		}
		
		/**
		 * Deletes a DisplayObject3D. 
		 * @param camera
		 * 
		 */
		public function deleteObject3D(name:String):void
		{
			delete settings.objects3D[name];
			
			sendNotification(Notifications.OBJECT3D_DELETED_MODEL, name);
		}
		
		/**
		 * Returns the list of 3D objects as a Dictionary. 
		 * @return 
		 * 
		 */
		public function get objects3D():Dictionary
		{
			
			return settings.objects3D;
		}
		
		/**
		 * Returns an Object3DVO for a given object3D name. 
		 * @param name
		 * 
		 */
		public function getObject3D(name:String):Object3DVO
		{
			var VO:Object3DVO = settings.objects3D[name].clone();
					
			return VO;
		}
		
		/**
		 * Stores a reference to the latest DisplayObject3D to be selected. 
		 * @param name
		 * 
		 */
		public function set selectedObject3D(name:String):void
		{
			settings.selectedObject3D = name;

		}
		
		/**
		 * Returns a reference to the latest DisplayObject3D to be selected. 
		 * @return 
		 * 
		 */
		public function get selectedObject3D():String
		{
			return settings.selectedObject3D;
		}
		
		/**
		 * Updates the position of an object3D. 
		 * @param name
		 * @param pos
		 * 
		 */
		public function setPosObject3D(name:String, pos:Number3D, update:Boolean = true):void
		{
			var VO:Object3DVO = settings.objects3D[name];

			if(VO == null)
			{
				return;
			}
			
			// ensure whole numbers are used
			pos.x = Math.round(pos.x);
			pos.y = Math.round(pos.y);
			pos.z = Math.round(pos.z);
			
			// only if anything has changed bother to update
			if(VO.x != pos.x || VO.y != pos.y || VO.z != pos.z)
			{	
				VO.x = pos.x;
				VO.y = pos.y;
				VO.z = pos.z;
				
				setPosObject3DInScene(name, pos);
				
				if(update)
				{
					sendNotification(Notifications.OBJECT3D_POSITION_CHANGED_MODEL, name);	
				}
			}
			
		}
		
		/**
		 * Update the rotational value of an object3D on a given axis. 
		 * @param name
		 * @param property
		 * @param value
		 * 
		 */
		public function setObject3DRotation(name:String, property:String, value:int):void
		{
			(settings.objects3D[name] as Object3DVO)[property] = value;
			
			var object3D:DisplayObject3D = getObject3DInScene(name);
			
			object3D[property] = value;
			
			sendNotification(Notifications.OBJECT3D_ROTATION_CHANGED_MODEL);
		}
		
		public function setObject3DLabel(name:String, value:String):void
		{
			var VO:Object3DVO = settings.objects3D[name];
			VO.label = value;

			sendNotification(Notifications.OBJECT3D_LABEL_CHANGED_MODEL, name);
		}
		
		public function setObject3DDimension(name:String, property:String, value:*):void
		{
			var VO:Object3DVO = getObject3D(name);;
			
			var object3D:DisplayObject3D = getObject3DInScene(name);
			
			if(property == "width")
			{
				VO.scaleX = value / VO[property];
				object3D.scaleX = VO.scaleX;
			}
			
			if(property == "height")
			{
				VO.scaleY = value / VO[property];
				object3D.scaleY = VO.scaleY;
			}
			
			if(property == "depth")
			{
				VO.scaleZ = value / VO[property];
				object3D.scaleZ = VO.scaleZ;
			}
			
			settings.objects3D[name] = VO;
			
			sendNotification(Notifications.OBJECT3D_DIMENSION_CHANGED_MODEL, name);
		}
		
		public function setDisplayFlatOnScreen(name:String, value:Boolean):void
		{
			if(settings.objects3D[name] is PlaneVO)
			{
				(settings.objects3D[name] as PlaneVO).billboard = value;
				
				sendNotification(Notifications.PLANE_PROJECTION_CHANGED_MODEL, (settings.objects3D[name] as PlaneVO).clone());
			}
			
		}
		
		public function setObject3DMaterial(object3D:Object3DVO, update:Boolean = true):void
		{
			var clone3D:Object3DVO = object3D.clone() as Object3DVO;
			
			settings.objects3D[clone3D.name] = clone3D;	

			if(update)
			{	
				sendNotification(Notifications.OBJECT3D_MATERIAL_UPDATED_MODEL, object3D.clone());
			} 
		} 
		
		public function setObject3DMaterialTransportControls(name:String, mat:MaterialVO):void
		{
			var object3D:Object3DVO = settings.objects3D[name];
			var existingMat:MaterialVO;
			
			if(object3D is SingleMaterialObject3DVO)
			{
				existingMat = (object3D as SingleMaterialObject3DVO).material;
			}
			
			if(object3D is MaterialsListObject3DVO)
			{
				existingMat = (object3D as MaterialsListObject3DVO).materialsList[(object3D as MaterialsListObject3DVO).selectedFace];
			}

			if(mat is VideoStreamMaterialVO)
			{
				(existingMat as VideoStreamMaterialVO).showControls = (mat as VideoStreamMaterialVO).showControls
			}
			
			if(mat is MovieMaterialVO)
			{
				(existingMat as MovieMaterialVO).showControls = (mat as MovieMaterialVO).showControls
			}
		}
		
		///////////////////////////////////////////////
		//
		// Viewport
		//
		///////////////////////////////////////////////
		
		/**
		 * Adds a Viewport3D reference to dictionary. 
		 * @param viewport
		 * 
		 */
		public function addViewport(viewport:ViewportVO):void
		{
			viewports[viewport.viewportID] = viewport.clone();
			sendNotification(Notifications.VIEWPORT_ADDED_MODEL, viewport.clone());
			
			/* var i:uint = 0;
			
			if(numViewports() == 4)
			{
				sendNotification(Notifications.DEFAULT_VIEWPORTS_ADDED);
			} */ 
		}
		
		/**
		 * Returns the complete list of viewports. 
		 * @return 
		 * 
		 */
		public function get viewports():Dictionary
		{
			return settings.viewports;
		}
		
		/**
		 * Clear all viewport VOs from model. 
		 * 
		 */
		public function clearAllViewports():void
		{
			for each(var viewport:ViewportVO in settings.viewports)
			{
				delete settings.viewports[viewport.viewportID];
			}
		}
		
		/**
		 * Returns the number of viewports added. Four are loaded by default. 
		 * @return 
		 * 
		 */
		public function numViewports():uint
		{
			var i:uint = 0;
			
			for each(var viewport:ViewportVO in settings.viewports)
			{
				i++;
			}
			
			return i;
		}
		
		/**
		 * Updates an existing viewport. 
		 * @param vVO
		 * 
		 */
		public function updateViewport(vVO:ViewportVO):void
		{
			(settings.viewports[vVO.viewportID] as ViewportVO).currentCamera = vVO.currentCamera;
		}
		
		/**
		 * Returns the viewport VO specified. 
		 * @param name
		 * @return 
		 * 
		 */
		public function getViewport(name:String):ViewportVO
		{
			return settings.viewports[name].clone();
		}
		
		///////////////////////////////////////////////
		//
		// Timeline
		//
		///////////////////////////////////////////////
		
		/**
		 * Stores the current time, (as defined by the thumb on the Timeline). 
		 * @param time
		 * 
		 */
		public function set currentTimeInterval(time:int):void
		{
			settings.currentTimeInterval = time;
		}
		
		/**
		 * Returns the current time, (as defined by the thumb on the Timeline). 
		 * @param time
		 * 
		 */
		public function get currentTimeInterval():int
		{
			return settings.currentTimeInterval;
		} 
		
		/**
		 * Adds a keyframe to an object3D. 
		 * @param kf
		 * @param name
		 * 
		 */
		public function addKeyframe(name:String, kf:KeyframeVO):void
		{
			var VO:Object3DVO = settings.objects3D[name];
			VO.keyframes[kf.id] = kf.clone();

			sendNotification(Notifications.TIMELINE_KEYFRAME_ADDED, kf.clone());
		}
		
		/**
		 * Removes specified keyframe from the object3D. 
		 * @param name
		 * @param keyframeID
		 * 
		 */
		public function removeKeyframe(name:String, keyframeID:String):void
		{
			var VO:Object3DVO = settings.objects3D[name];
			var keyframes:Dictionary = VO.keyframes;

			delete keyframes[keyframeID];
			
			sendNotification(Notifications.TIMELINE_KEYFRAME_REMOVED, VO.clone());
		}
		
		/**
		 * Updates the time of keyframe which has been moved. 
		 * 
		 */
		public function updateKeyframeTime(name:String, keyframeID:String, time:int):void
		{
			var kf:KeyframeVO = ((settings.objects3D[name] as Object3DVO).keyframes[keyframeID] as KeyframeVO);
			kf.time = time;
			
			sendNotification(Notifications.TIMELINE_KEYFRAME_UPDATED, kf.clone());
		}
		
		/**
		 * Updates a specified keyframe on an object3D with new position information. 
		 * @param name
		 * @param keyframeID
		 * @param position
		 * @return 
		 * 
		 */
		public function updateKeyframePosition(name:String, keyframeID:String, position:Number3D):void
		{
			var kf:KeyframeVO = ((settings.objects3D[name] as Object3DVO).keyframes[keyframeID] as KeyframeVO);
			kf.position = position;
			
			sendNotification(Notifications.TIMELINE_KEYFRAME_UPDATED, kf.clone());
		}
		
		/**
		 * Adds a addHideMarker to an object3D. 
		 * @param kf
		 * @param name
		 * 
		 */
		public function addHideMarker(name:String, hm:HideMarkerVO):void
		{
			var VO:Object3DVO = settings.objects3D[name];
			hm.id = "hideMarker_" + _uniqueID.generateID();
			VO.hideMarkers[hm.id] = hm.clone();

			sendNotification(Notifications.TIMELINE_HIDE_MARKER_ADDED, hm.clone());
		}
		
		/**
		 * Removes specified hideMarker from the object3D. 
		 * @param name
		 * @param keyframeID
		 * 
		 */
		public function removeHideMarker(name:String, hideMarkerID:String):void
		{
			var VO:Object3DVO = settings.objects3D[name];
			var hideMarkers:Dictionary = VO.hideMarkers;

			delete hideMarkers[hideMarkerID];
			
			sendNotification(Notifications.TIMELINE_HIDE_MARKER_REMOVED, VO.clone());
		}
		
		/**
		 * Updates the begin and end time of a hide marker which has been moved. 
		 * 
		 */
		public function updateHideMarkerTime(name:String, hideMarkerID:String, begin:int, end:int):void
		{
			var hm:HideMarkerVO = ((settings.objects3D[name] as Object3DVO).hideMarkers[hideMarkerID] as HideMarkerVO);
			hm.begin = begin;
			hm.end = end;
			
			sendNotification(Notifications.TIMELINE_HIDE_MARKER_UPDATED, hm.clone());
		}
		
		/**
		 * Adds a global marker. 
		 * @param kf
		 * @param name
		 * 
		 */
		public function addGlobalMarker(gm:GlobalMarkerVO):void
		{
			gm.id = "globalMarker_" + _uniqueID.generateID();
			settings.globalMarkers[gm.id] = gm.clone();

			sendNotification(Notifications.TIMELINE_GLOBAL_MARKER_ADDED, gm.clone());
		}
		
		/**
		 * Removes specified global marker. 
		 * @param name
		 * @param keyframeID
		 * 
		 */
		public function removeGlobalMarker(globalMarkerID:String):void
		{
			var globalMarkers:Dictionary = settings.globalMarkers;

			delete globalMarkers[globalMarkerID];
			
			sendNotification(Notifications.TIMELINE_GLOBAL_MARKER_REMOVED);
		}
		
		/**
		 * Updates the begin and end time of a hide marker which has been moved. 
		 * 
		 */
		public function updateGlobalMarkerTime(globalMarkerID:String, time:int):void
		{
			var gm:GlobalMarkerVO = settings.globalMarkers[globalMarkerID] as GlobalMarkerVO;

			gm.time = time;
			
			sendNotification(Notifications.TIMELINE_GLOBAL_MARKER_UPDATED, gm.clone());
		}
		
		public function get globalMarkers():Dictionary
		{
			return settings.globalMarkers;
		} 
		
		/**
		 * Adds a tween to the tweens array of a specified object3D. 
		 * @param name
		 * @param tween
		 * 
		 */
		public function addTween(name:String, tween:Number3D):void
		{
			((settings.objects3D[name] as Object3DVO).tweens as Array).push(tween.clone());
		}
		
		/**
		 * Remove all tweens. 
		 * @param name
		 * 
		 */
		public function removeAllTweens(name:String):void
		{	
			var tweens:Array = ((settings.objects3D[name] as Object3DVO).tweens as Array);
			
			for(var i:uint=0; i<tweens.length; i++)
			{
				tweens.splice(0, 1);
				i--;
			}
		}
		///////////////////////////////////////////////
		//
		// File
		//
		///////////////////////////////////////////////
		
		/**
		 * Sets the save path. 
		 * @param path
		 * 
		 */
		public function set savePath(path:String):void
		{
			settings.savePath = path;
		}
		
		/**
		 * Returns stored save path or null if one has not been set 
		 * @return 
		 * 
		 */
		public function get savePath():String
		{
			return settings.savePath;
		}
		
		///////////////////////////////////////////////
		//
		// XML
		//
		///////////////////////////////////////////////
		
		/**
		 * Returns a description of the project as XML 
		 * @return 
		 * 
		 */
		public function getProjectXML():XML
		{
			var projectXML:XML = new XML(<project id={ProjectConstants.PROJECT_NAME}></project>);
			var i:uint = 0;
			
			// do stage			
			projectXML.appendChild(settings.stageProperties.toXML());
			
			// do viewports
			projectXML.appendChild(<viewports></viewports>);
			
			for each(var viewport:ViewportVO in settings.viewports)
			{
				projectXML.viewports.appendChild(<viewport id={viewport.viewportID}></viewport>);
				projectXML.viewports.viewport[i].appendChild(<currentCamera>{viewport.currentCamera}</currentCamera>);
				
				i++;
			}
			
			// do global markers
			i=0;
			projectXML.appendChild(<globalMarkers></globalMarkers>);
			
			for each(var gMarker:GlobalMarkerVO in settings.globalMarkers)
			{
				projectXML.globalMarkers.appendChild(<globalMarker id={gMarker.id}></globalMarker>);
				projectXML.globalMarkers.globalMarker[i].appendChild(<time>{gMarker.time}</time>);
				
				i++;
			}
			
			// do cameras
			projectXML.appendChild(<cameras></cameras>);
			
			for each(var camera:CameraVO in settings.cameras)
			{
				if(camera is ActualViewCameraVO)
				{
					projectXML.cameras.appendChild((camera as ActualViewCameraVO).toXML());
				}
				
				if(camera is MainViewCameraVO)
				{
					projectXML.cameras.appendChild((camera as MainViewCameraVO).toXML());
				}
				
				if(camera is TopViewCameraVO)
				{
					projectXML.cameras.appendChild((camera as TopViewCameraVO).toXML());
				}
				
				if(camera is FrontViewCameraVO)
				{
					projectXML.cameras.appendChild((camera as FrontViewCameraVO).toXML());
				}
				
				if(camera is LeftViewCameraVO)
				{
					projectXML.cameras.appendChild((camera as LeftViewCameraVO).toXML());
				}
			}
			
			projectXML.appendChild(<objects3D></objects3D>);
			
			for each(var object3D:Object3DVO in settings.objects3D)
			{
				if(object3D is PlaneVO)
				{
					projectXML.objects3D.appendChild((object3D as PlaneVO).toXML());
				}
				
				if(object3D is CubeVO)
				{
					projectXML.objects3D.appendChild((object3D as CubeVO).toXML());
				}
				
				if(object3D is SphereVO)
				{
					projectXML.objects3D.appendChild((object3D as SphereVO).toXML());
				}
				
				if(object3D is CameraProxyVO)
				{
					projectXML.objects3D.appendChild((object3D as CameraProxyVO).toXML());
				}
				
				if(object3D is TargetProxyVO)
				{
					projectXML.objects3D.appendChild((object3D as TargetProxyVO).toXML());
				}
			}
			
			
			return projectXML;
		}
		
		///////////////////////////////////////////////
		//
		// Utilities
		//
		///////////////////////////////////////////////
		
		public function calculateTweens(name:String):void
		{
			var VO:Object3DVO = getObject3D(name);
			var keyframes:Dictionary = VO.keyframes;

			var keyframesAC:ArrayCollection = new ArrayCollection();
			
			for each(var kf:KeyframeVO in keyframes)
			{
				keyframesAC.addItem(kf);
			}
			
			//Sort keyFrames time ascending
			var s:Sort = new Sort();
			var sF:SortField = new SortField("time", true);
			sF.numeric = true;				
			s.fields = [sF];
			keyframesAC.sort = s;
			keyframesAC.refresh();
			
			//Tween
			if(keyframesAC.length < 1)
			{
				return;
			}
			
			var evt:Event = new Event("event");
			var startPos:Number3D = ((keyframesAC[0] as KeyframeVO).position).clone();
			
			removeAllTweens(VO.name);
			
			addTween(VO.name, startPos);
			
			for(var i:uint=0; i<keyframesAC.length-1; i++)
			{
				var currentPos:Number3D = (keyframesAC[i].position as Number3D).clone();
				var nextPos:Number3D = (keyframesAC[i+1].position as Number3D).clone();
				var t:uint = (keyframesAC[i+1].time - keyframesAC[i].time);

				if(t==0)
				{
					return;
				}
				
				Tweener.addTween(currentPos, {x:nextPos.x, y:nextPos.y, z:nextPos.z, time:t, useFrames:true, transition:"linear"});
				
				for(var j:uint=0; j<t; j++)
				{
					Tweener.onEnterFrame(evt);
					
					addTween(VO.name, currentPos.clone());
					
				}
				
			}
			
			Tweener.removeTweens(currentPos);
		}
		
		protected function onFileLoadComplete():void
		{
			sendNotification(Notifications.OBJECT3D_MATERIAL_UPDATED_MODEL);
		}
			
		/**
		 * Returns typed reference to the value object. 
		 * @return 
		 * 
		 */
		protected function get settings():ProjectSettingsVO
		{
			return getData() as ProjectSettingsVO;
		}
		
		/**
		 * Returns the DisplayObject3D in the scene, given its name. 
		 * @param name
		 * 
		 */		
		protected function getObject3DInScene(name:String):DisplayObject3D
		{
			var scene:Scene3D = settings.scene;
			
			return scene.getChildByName(name);
		}
		
		
	}
}